#include "config.h"

#include <sys/types.h>
#include <errno.h>

#define DBG_SUBSYS S_LIBCLUSTER

#include "metadata.h"
#include "net_global.h"
#include "cluster.h"
#include "../nodepool/diskmap.h"
#include "dispatch.h"
#include "node.h"
#include "ynet_rpc.h"
#include "job_dock.h"
#include "dbg.h"

static int inited;

int dispatch_srv_newid(chkid_t *chkid)
{
        int ret;

        if (inited == 0) {
                ret = ENOSYS;
                GOTO(err_ret, ret);
        }

        ret = node_srv_admin_newid(chkid);
        if (unlikely(ret)) {
                GOTO(err_ret, ret);
        }

        return 0;
err_ret:                       
        return ret;
}

int dispatch_srv_writeable(const nid_t *diskid, int count)
{
        int ret;

        if (inited == 0) {
                ret = ENOSYS;
                GOTO(err_ret, ret);
        }

        ret = nodetable_writeable(diskid, count);
        if (unlikely(ret)) {
                GOTO(err_ret, ret);
        }

        return 0;
err_ret:
        return ret;
}

int dispatch_srv_netinfo(ynet_net_info_t *info, const nid_t *nid)
{
        int ret;
        uint32_t len = MAX_BUF_LEN;

        if (inited == 0) {
                ret = ENOSYS;
                GOTO(err_ret, ret);
        }

        if (net_islocal(nid)) {
                ret = rpc_getinfo((void *)info, &len);
                if (unlikely(ret))
                        GOTO(err_ret, ret);
        } else {
                ret = netable_getinfo(nid, (void *)info, &len);
                if (unlikely(ret)) {
                        if (ret == ENONET) {
                                ret = ENOKEY;
                        }

                        GOTO(err_ret, ret);
                }
        }

        YASSERT(strcmp(info->name, "none"));

        return 0;
err_ret:
        return ret;
}

int dispatch_srv_check_storage_area(const char *site_name)
{
        int ret;

        ret = diskmap_check_insite(site_name);
        if (unlikely(ret)) {
                GOTO(err_ret, ret);
        }

        return 0;
err_ret:
        return ret;
}

int dispatch_srv_list_storage_area(char *buf, int *count)
{
        int ret;

        ret = diskmap_list_insite(buf, count);
        if (unlikely(ret)) {
                GOTO(err_ret, ret);
        }

        return 0;
err_ret:
        return ret;
}

int dispatch_srv_heartbeat(const char *name, const char *dfinfo, const nodestat_t *_stat, const char *status,
                           uint32_t *admin_uptime)
{
        int ret;
        nodestat_t stat;

        if (inited == 0) {
                ret = ENOSYS;
                GOTO(err_ret, ret);
        }

        stat = *_stat;
        stat.type = node_type1(status);
        ret = nodetable_update(name, dfinfo, &stat);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        ret = node_srv_admin_version(admin_uptime);
        if (unlikely(ret)) {
                UNIMPLEMENTED(__DUMP__);
        }

        return 0;
err_ret:
        return ret;
}

int dispatch_srv_vol_notifiction(const char *name, const local_vol_t *vols, uint32_t options)
{
        int ret;

        DINFO("node:%s vol notifiction:%llu options:%d \n", name, (LLU)vols->volcount, options);

        ret = nodetable_update_local_vols(name, vols, options);
        if (unlikely(ret)) {
                GOTO(err_ret, ret);
        }

#if 0
        nodetable_dump();
#endif

        return 0;
err_ret:
        return ret;
}


static int __dispatch_srv_newdisk_errno(int err)
{
        uint32_t last_reset;

        /**
         * io interrupt too long time when rack down in vdbench test.
         * pool & volume cannot load, because ENOSPC change to EAGAIN cannot set chunk to dirty.
         */
        return err;

        if (err == ENOSPC) {
                last_reset = nodetable_last_reset();

                if (gettime() - last_reset < 20)
                        return EAGAIN;
                else
                        return ENOSPC;
        } else
                return err;
}

/**
 * 分配chunk副本位置数组
 *
 * @param diskid
 * @param _count
 * @param site_name
 * @param inrack 一副本位置，限定保护域
 * @param skip 已有的，检查是否有效，参与计数
 * @param skip_count
 * @param force 数量必须够，否则返回NOSPC
 *
 * @return
 */
int dispatch_srv_newdisk(const char *pool, diskid_t *diskid, int repnum,
                         const nid_t *skip, int skip_count, int flag)
{
        int ret, i;
        uint32_t j;

        ret = nodetable_newdisk(pool, diskid, repnum, skip, skip_count, flag);
        if (unlikely(ret)) {
                GOTO(err_ret, ret);
        }

        for (i = 0; i < repnum; i++) {
                YASSERT(!net_isnull(&diskid[i]));
                for (j = 0; j < skip_count; j++) {
                        YASSERT(nid_cmp(&diskid[i], &skip[j]));
                }
        }

        return 0;
err_ret:
        return __dispatch_srv_newdisk_errno(ret);
}

int dispatch_srv_init()
{
        inited = 1;

        return 0;
}

void dispatch_srv_destroy()
{
        inited = 0;
}

static int __dispatch_srv_sysstat(lich_stat_t *stat)
{
        int ret;

        ret = diskmap_online(&stat->site_online, &stat->rack_online,
                             &stat->node_online, &stat->disk_online);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        ret = diskmap_total(&stat->site_total, &stat->rack_total,
                            &stat->node_total, &stat->disk_total);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        return 0;
err_ret:                       
        return ret;
}

int dispatch_srv_sysstat(lich_stat_t *stat, uint32_t force)
{
        int ret;
        static lich_stat_t prev;
        static time_t prev_load = 0;
        time_t now = 0;

        if (inited == 0) {
                ret = ENOSYS;
                GOTO(err_ret, ret);
        }

        now = gettime();
        if (now - prev_load > 30 || force) {
                prev_load = now;

                ret = __dispatch_srv_sysstat(&prev);
                if (unlikely(ret))
                        GOTO(err_ret, ret);
        }

        *stat = prev;

        return 0;
err_ret:                       
        return ret;
}

int dispatch_srv_pooldump(const char *pool)
{
        return poolmap_dump(pool);
}

int dispatch_srv_addnode(const char *name,  const nid_t *nid)
{
        (void) name;
        (void) nid;

        UNIMPLEMENTED(__DUMP__);

        return 0;
}

int dispatch_srv_delnode(const char *name)
{
        int ret;

        ret = nodetable_del(name);
        if (unlikely(ret)) {
                GOTO(err_ret, ret);
        }

        return 0;
err_ret:
        return ret;
}
